﻿Imports System.DirectoryServices.ActiveDirectory
Imports System.Net
Imports System.Net.Sockets

''' <summary>
''' ドメイン関連情報画面のフォームです。
''' </summary>
''' <remarks></remarks>
Public Class DomainInfo
  Inherits Page

#Region " イベントハンドラ "
  Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    If Me.IsPostBack = False Then   '初期表示の時
      Using domain = DirectoryAccess.GetDomain()  'ドメインを取得
        If domain IsNot Nothing Then
          Me.ShowInfo(domain)   'ドメイン関連情報を表示
        End If
      End Using
    End If
  End Sub

  Protected Sub GCListBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles GCListBox.SelectedIndexChanged
    Me.GCSiteLabel.Text = DirectCast(ViewState("GCSites"), IList(Of String)).Item(Me.GCListBox.SelectedIndex)
  End Sub

  Protected Sub SiteListBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles SiteListBox.SelectedIndexChanged
    Dim sites = DirectCast(ViewState("Sites"), Dictionary(Of String, Tuple(Of List(Of String), 
      Dictionary(Of String, Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType)), List(Of String))))
    Dim site = sites.Item(Me.SiteListBox.SelectedValue)
    Me.SetSite(site)  'サイトを設定
  End Sub

  Protected Sub SiteLinkListBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles SiteLinkListBox.SelectedIndexChanged
    Dim sites = DirectCast(ViewState("Sites"), Dictionary(Of String, Tuple(Of List(Of String), 
      Dictionary(Of String, Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType)), List(Of String))))
    Dim site = sites.Item(Me.SiteListBox.SelectedValue)
    Dim siteLinkInfo = site.Item2.Item(Me.SiteLinkListBox.SelectedValue)
    Me.SetSiteLinkInfo(siteLinkInfo)  'サイトリンク情報を設定
  End Sub

  Protected Sub DCListBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles DCListBox.SelectedIndexChanged
    Me.SetDCInfo()  'ドメインコントローラ情報を設定
  End Sub

  Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Response.Redirect("Main.aspx")
  End Sub
#End Region

#Region " プライベートメソッド "
  ''' <summary>
  ''' ドメイン関連情報を表示します。
  ''' </summary>
  ''' <param name="adDomain">ドメイン。</param>
  ''' <remarks></remarks>
  Private Sub ShowInfo(adDomain As Domain)
    With adDomain
      With .Forest
        '****************************** フォレスト ******************************
        Me.ForestLabel.Text = .Name
        Me.ForestModeLabel.Text = .ForestMode.ToString()
        Me.RootDomainLabel.Text = .RootDomain.Name
        Me.SetDataSource(Me.GCListBox,
          .GlobalCatalogs.Cast(Of GlobalCatalog)().Select(Function(gc) gc.Name).ToList())   'データソースを設定

        Dim gcSites = .GlobalCatalogs.Cast(Of GlobalCatalog)().Select(Function(gc) gc.SiteName).ToList()  'サイトリスト
        ViewState("GCSites") = gcSites
        Me.GCSiteLabel.Text = gcSites.Item(0)
        Me.SetDataSource(Me.DomainListBox,
          .Domains.Cast(Of Domain)().Select(Function(dom) dom.Name).ToList())   'データソースを設定

        '****************************** サイト ******************************
        'サイト名/サイト項目
        Dim sites As New Dictionary(Of String, Tuple(Of List(Of String), 
          Dictionary(Of String, Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType)), List(Of String)))()
        'サーバのリスト, Dictionary(サイトリンク名, Tuple(コスト, 複製間隔, 転送の種類)), サブネットのリスト
        Dim siteItem As Tuple(Of List(Of String), 
            Dictionary(Of String, Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType)), List(Of String))

        For Each site As ActiveDirectorySite In .Sites    'サイト数分
          Dim servers = site.Servers.Cast(Of DirectoryServer)().Select(Function(server) server.Name).ToList()
          Dim siteLinks = site.SiteLinks.Cast(Of ActiveDirectorySiteLink)().ToDictionary(Function(sl) sl.Name,
            Function(sl) New Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType)(
              sl.Cost, sl.ReplicationInterval, sl.TransportType))
          Dim subnets = site.Subnets.Cast(Of ActiveDirectorySubnet)().Select(Function(subnet) subnet.Name).ToList()

          siteItem = New Tuple(Of List(Of String), 
            Dictionary(Of String, Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType)), 
            List(Of String))(servers, siteLinks, subnets)
          sites.Add(site.Name, siteItem)
        Next
        ViewState("Sites") = sites
        siteItem = sites.Item(.Sites.Item(0).Name)
        Me.SetDataSource(Me.SiteListBox, sites.Keys)  'データソースを設定
        Me.SetSite(siteItem)  'サイトを設定
      End With

      '****************************** ドメイン ******************************
      Me.DomainLabel.Text = .Name
      Me.DomainModeLabel.Text = .DomainMode.ToString()
      'ドメインコントローラー名/Tuple(OS, IPアドレス, サイト名)
      Dim dcInfos = .DomainControllers.Cast(Of DomainController)().ToDictionary(
        Function(dc) dc.Name.Substring(0, dc.Name.IndexOf("."c)),
        Function(dc) New Tuple(Of String, String, String)(dc.OSVersion, GetDomainControllerIPAddress(dc), dc.SiteName))
      ViewState("DCInfos") = dcInfos
      Me.SetDataSource(Me.DCListBox, dcInfos.Keys)  'データソースを設定
      Me.SetDCInfo()  'ドメインコントローラー情報を設定

      '****************************** FSMO ******************************
      Me.SchemaLabel.Text = .Forest.SchemaRoleOwner.Name
      Me.NamingLabel.Text = .Forest.NamingRoleOwner.Name
      Me.RIDLabel.Text = .RidRoleOwner.Name
      Me.PDCLabel.Text = .PdcRoleOwner.Name
      Me.InfraLabel.Text = .InfrastructureRoleOwner.Name
    End With
  End Sub

  ''' <summary>
  ''' ドメインコントローラーの IP アドレスを取得します。
  ''' </summary>
  ''' <param name="dc">ドメインコントローラー。</param>
  ''' <returns>ドメインコントローラーの IP アドレス。</returns>
  ''' <remarks></remarks>
  Private Function GetDomainControllerIPAddress(dc As DomainController) As String
    If dc.IPAddress.Contains(".") Then  'IPv4の時
      Return dc.IPAddress
    End If

    Dim ip = IPAddress.Parse(dc.IPAddress)
    If IPAddress.IsLoopback(ip) Then  'ループバックの時
      Dim addresses = Dns.GetHostAddresses(String.Empty)  'ローカルホストのIPアドレス
      ip = addresses.FirstOrDefault(Function(address) address.AddressFamily = AddressFamily.InterNetwork)   'IPv4のアドレス
      If ip Is Nothing Then   'IPv4のアドレスがない時
        ip = addresses(0)   '先頭のIPv6のアドレス
      End If
      Return ip.ToString()
    End If
    Return dc.IPAddress
  End Function

  ''' <summary>
  ''' リストボックスにデータソースを設定します。
  ''' </summary>
  ''' <param name="listBox">リストボックス。</param>
  ''' <param name="dataSource">データソースとなる文字列のコレクション。</param>
  ''' <remarks></remarks>
  Private Sub SetDataSource(listBox As ListBox, dataSource As IEnumerable(Of String))
    listBox.DataSource = dataSource
    listBox.DataBind()
    listBox.Items.Item(0).Selected = listBox.AutoPostBack
  End Sub

  ''' <summary>
  ''' サイトを設定します。
  ''' </summary>
  ''' <param name="site">サイト項目。</param>
  ''' <remarks></remarks>
  Private Sub SetSite(site As Tuple(
    Of List(Of String), Dictionary(Of String, Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType)), List(Of String)))

    Me.SetDataSource(Me.ServerListBox, site.Item1)  'データソースを設定
    Me.SetDataSource(Me.SiteLinkListBox, site.Item2.Keys)   'データソースを設定

    Dim siteLinkInfo = site.Item2.Item(Me.SiteLinkListBox.SelectedValue)
    Me.SetSiteLinkInfo(siteLinkInfo)  'サイトリンク情報を設定
    Me.SetDataSource(Me.SubnetListBox, site.Item3)  'データソースを設定
  End Sub

  ''' <summary>
  ''' サイトリンク情報を設定します。
  ''' </summary>
  ''' <param name="siteLinkInfo">サイトリンク情報。</param>
  ''' <remarks></remarks>
  Private Sub SetSiteLinkInfo(siteLinkInfo As Tuple(Of Integer, TimeSpan, ActiveDirectoryTransportType))
    Me.CostLabel.Text = siteLinkInfo.Item1.ToString()
    Me.RepliLabel.Text = siteLinkInfo.Item2.ToString()
    Me.TransportLabel.Text = siteLinkInfo.Item3.ToString()
  End Sub

  ''' <summary>
  ''' ドメインコントローラー情報を設定します。
  ''' </summary>
  ''' <remarks></remarks>
  Private Sub SetDCInfo()
    Dim dcInfo = DirectCast(ViewState("DCInfos"), 
      Dictionary(Of String, Tuple(Of String, String, String))).Item(Me.DCListBox.SelectedValue)
    Me.OSLabel.Text = dcInfo.Item1
    Me.IPLabel.Text = dcInfo.Item2
    Me.DCSiteLabel.Text = dcInfo.Item3
  End Sub
#End Region
End Class